package part9.interpreter;

import part9.ast.*;

import java.util.HashMap;
import java.util.Map;

public class Interpreter extends NodeVisitor {
    private final Parser parser;
    private Map<String, Object> globalScope = new HashMap<>();

    public Interpreter(Parser parser) {
        this.parser = parser;
    }
    @Override
    Object visitBinOp(AST node) throws Exception {
        BinOp binOp = (BinOp)node;
        int res = 0;
        switch (binOp.op.type) {
            case PLUS:
                res = (Integer)visit(binOp.left) + (Integer)visit(binOp.right);
                break;
            case MINUS:
                res = (Integer)visit(binOp.left) - (Integer)visit(binOp.right);
                break;
            case MUL:
                res = (Integer)visit(binOp.left) * (Integer)visit(binOp.right);
                break;
            case DIV:
                res = (Integer)visit(binOp.left) / (Integer)visit(binOp.right);
                break;
        }
        return res;
    }

    @Override
    Object visitNum(AST node) {
        return ((Num)node).value;
    }

    @Override
    Object visitUnaryOp(AST node) throws Exception {
        UnaryOp unaryOp = (UnaryOp)node;
        int res = 0;
        switch (unaryOp.op.type) {
            case PLUS:
                res = +(Integer)visit(unaryOp.expr);
                break;
            case MINUS:
                res = -(Integer)visit(unaryOp.expr);
                break;
        }
        return res;
    }

    @Override
    Object visitCompound(AST node) throws Exception {
        Compound compound = (Compound) node;
        for (AST child : compound.children) {
            visit(child);
        }
        return null;
    }

    @Override
    Object visitAssign(AST node) throws Exception {
        Assign assign = (Assign)node;
        String varName = assign.left.value;
        globalScope.put(varName,visit(assign.right));
        return null;
    }

    @Override
    Object visitVar(AST node) throws Exception {
        Var var = (Var)node;
        String varName = var.value;
        Object varValue = globalScope.get(varName);
        if (varValue==null){
            throw new Exception("Name Error: No this kind of var.");
        }
        else {
            return varValue;
        }
    }

    @Override
    Object visitNoOp(AST node) throws Exception {
        return null;
    }

    public Object interpret() throws Exception {
        AST tree = parser.parse();
        if (tree == null) {
            return "";
        }
        else {
            return visit(tree);
        }
    }

    public Map<String, Object> getGlobalScope() {
        return globalScope;
    }
}
